﻿using System.Collections;
using System.Text;

namespace BullsEyeGame
{
    internal class ConsoleUI
    {
        private const bool k_Won = true;
        private const bool k_Quit = true;
        private const bool k_Valid = true;
        private const bool k_Play = true;
        private const bool k_RanOut = true;
        private const int k_NumberOfSeparatorsInLine = 3;

        private GameLogics m_GameLogics = GameLogics.Instance;
        private bool m_GameWon;
        private bool m_RanOutOfGuesses;
        private bool m_QuitGame;

        public void RunGame()
        {
            bool playAnotherGame = !k_Play;
            int guessesCounter;
            int maxNumberOfGuessesInCurrentGame;

            do
            {
                initGame();
                maxNumberOfGuessesInCurrentGame = m_GameLogics.MaxNumberOfGuessesChosen;
                for (guessesCounter = 0; guessesCounter < maxNumberOfGuessesInCurrentGame && !m_GameWon && !m_QuitGame; ++guessesCounter)
                {
                    drawTable();
                    playTurn();
                }

                m_RanOutOfGuesses = (guessesCounter == maxNumberOfGuessesInCurrentGame) && !m_GameWon;
                drawTable();
                printFinalGameMessage(guessesCounter);
                playAnotherGame = promptPlayAnotherGame();
            } while (playAnotherGame);
        }

        private void initGame() 
        { 
            bool inputCorrect = true;
            bool isParseOK = true;
            int maxNumberOfGuesses = GameLogics.k_MaxNumberOfGuesses;
            int minNumberOfGuesses = GameLogics.k_MinNumberOfGuesses;
            int numberOfGuesses;

            // reset global game loop status
            m_QuitGame = !k_Quit;
            m_GameWon = !k_Won;
            m_RanOutOfGuesses = !k_RanOut;
            Ex02.ConsoleUtils.Screen.Clear();

            do
            {
                string numberOfGuessesString;
                do
                {
                    System.Console.WriteLine(
                    "Please enter the number of guesses ({0}-{1}):", minNumberOfGuesses, maxNumberOfGuesses);
                    numberOfGuessesString = System.Console.ReadLine();
                    isParseOK = int.TryParse(numberOfGuessesString, out numberOfGuesses);
                } while (!isParseOK);                

                // check if number of guesses is valid
                inputCorrect = m_GameLogics.IsValidNumberOfGuessesRange(numberOfGuesses);
            } while (!inputCorrect);

            m_GameLogics.StartNewGame(numberOfGuesses);            
        }

        private void playTurn()
        {
            Guess newGuess = null;
            bool isValidGuess = k_Valid;
            bool isValidGuessFormat;
            bool isValidGuessContent = k_Valid;
            string playerNewGuessString;

            do
            {
                isValidGuessFormat = k_Valid;
                System.Console.WriteLine("Please type your next guess (A B C D) or 'Q' to quit");
                playerNewGuessString = System.Console.ReadLine().Trim();
                if (playerNewGuessString.Equals("Q") || playerNewGuessString.Equals("q"))
                {
                    m_QuitGame = k_Quit;
                    break;
                }
                else
                {
                    // Validate the the string is of format (A B C D)
                    isValidGuessFormat &= playerNewGuessString.Length == (2 * Guess.k_GuessLength) - 1; // length check
                    isValidGuessFormat &= containsLettersOnly(playerNewGuessString);                    // is letter check
                    isValidGuessFormat &= isSpacedCorrectly(playerNewGuessString);                      // spacing checked
                    isValidGuessFormat &= playerNewGuessString.Equals(playerNewGuessString.ToUpper());  // case check
                    if (isValidGuessFormat)
                    {
                        playerNewGuessString = playerNewGuessString.Replace(" ", string.Empty);
                        newGuess = Guess.CreateGuess(playerNewGuessString);
                        isValidGuessContent = newGuess != null;
                    }                    
                }

                isValidGuess = isValidGuessFormat && isValidGuessContent;
                if (!isValidGuessFormat)
                {
                    System.Console.WriteLine("Wrong format - Enter exactly {0} capital letters with one space between them.", Guess.k_GuessLength);
                }
                else
                {
                    if (!isValidGuessContent)
                    {
                        char firstLetter = Guess.GetValidCharsAt(0);
                        char lastLetter = Guess.GetValidCharsAt(Guess.k_ValidChars.Length - 1);

                        System.Console.WriteLine("Illegal letters - Range allowed {0}-{1}.", firstLetter, lastLetter);
                    }
                }
            } while (!isValidGuess && !m_QuitGame);

            if (m_QuitGame == !k_Quit)
            {
                GuessResult currentGuessResult = m_GameLogics.AddNewGuess(newGuess);
                if (currentGuessResult.isMatch())
                {
                    m_GameWon = k_Won;
                }
            }            
        }

        private bool containsLettersOnly(string i_StringToCheck)
        {
            bool lettersOnly = true;

            for (int i = 0; i < i_StringToCheck.Length; i = i + 2)
            {
                lettersOnly = lettersOnly && char.IsLetter(i_StringToCheck.ToCharArray()[i]);
            }

            return lettersOnly;
        }

        private bool isSpacedCorrectly(string i_StringToCheck)
        {
            bool spacedCorrectly = true;

            for (int i = 1; i < i_StringToCheck.Length; i = i + 2)
            {
                spacedCorrectly = spacedCorrectly && (i_StringToCheck.ToCharArray()[i] == ' ');
            }

            return spacedCorrectly;
        }

        private void drawTable()
        {
            Ex02.ConsoleUtils.Screen.Clear();

            drawTableTitle();            
            drawTableHeaders();
            drawTargetGuessRowOfTable();
            drawTableBody();
        }

        private void drawTableTitle()
        {
            System.Console.WriteLine("Current board status:");
            System.Console.WriteLine();
        }

        private void drawTableHeaders()
        {
            int numberOfCharsInFirstColumn = (Guess.k_GuessLength * 2) + 1;
            int numberOfCharsInSecondColumn = (Guess.k_GuessLength * 2) - 1;
            StringBuilder tableHeaderLine = new StringBuilder("|");
            string pinsString = "Pins:";
            string resultString = "Result:";
            
            tableHeaderLine.Append(pinsString);
            for (int i = 0; i < numberOfCharsInFirstColumn - pinsString.Length; ++i)
            {
                tableHeaderLine.Append(' ');
            }

            tableHeaderLine.Append("|");
            tableHeaderLine.Append(resultString);
            for (int i = 0; i < numberOfCharsInSecondColumn - resultString.Length; ++i)
            {
                tableHeaderLine.Append(' ');
            }

            tableHeaderLine.Append("|");
            System.Console.WriteLine(tableHeaderLine);
            drawLineOfTableContainingCharacter('=');
        }

        private void drawTargetGuessRowOfTable()
        {
            int numberOfCharsInSecondColumn = (Guess.k_GuessLength * 2) - 1;
            StringBuilder targetGuessLine = new StringBuilder();
            targetGuessLine.Append("| ");

            if (m_RanOutOfGuesses || m_QuitGame)
            {
                // print with original targetGuess
                Guess targetGuess = m_GameLogics.TargetGuess;
                for (int i = 0; i < Guess.k_GuessLength; ++i)
                {
                    targetGuessLine.Append(targetGuess.At(i));
                    targetGuessLine.Append(' ');
                }
            }
            else
            {
                // print with encrypted targetGuess
                for (int i = 0; i < Guess.k_GuessLength; ++i)
                {
                    targetGuessLine.Append("# ");
                }
            }

            // print empty column to the right
            targetGuessLine.Append('|');
            for (int i = 0; i < numberOfCharsInSecondColumn; ++i)
            {
                targetGuessLine.Append(' ');
            }

            targetGuessLine.Append("|");
            System.Console.WriteLine(targetGuessLine);
            drawLineOfTableContainingCharacter('=');
        }

        private void drawTableBody()
        {
            int maxNumberOfGuessesChosen = m_GameLogics.MaxNumberOfGuessesChosen;
            ArrayList allGuessResults = m_GameLogics.GuessesResults;

            foreach (GuessResult currentGuessResult in allGuessResults)
            {
                drawFullRowOfTable(currentGuessResult);
            }

            for (int i = 0; i < maxNumberOfGuessesChosen - allGuessResults.Count; ++i)
            {
                drawEmptyRowOfTable();
            }

            System.Console.WriteLine();
        }

        private void drawFullRowOfTable(GuessResult i_GuessResult)
        {
            StringBuilder fullGuessLine = new StringBuilder("| ");
            int numberOfCharsInFirstColumn = (Guess.k_GuessLength * 2) + 1;
            int numberOfCharsInSecondColumn = (Guess.k_GuessLength * 2) - 1;

            for (int i = 0; i < Guess.k_GuessLength; ++i)
            {
                fullGuessLine.Append(i_GuessResult.Guess.At(i));
                fullGuessLine.Append(" ");
            }

            fullGuessLine.Append("|");
            
            for (int i = 0; i < i_GuessResult.NumberOfV; ++i)
            {
                fullGuessLine.Append("V");
                fullGuessLine.Append(" ");
            }

            if (i_GuessResult.NumberOfV == Guess.k_GuessLength)
            {
                fullGuessLine.Remove(fullGuessLine.Length - 1, 1); // removes possible no needed space
            }

            for (int i = 0; i < i_GuessResult.NumberOfX; ++i)
            {
                fullGuessLine.Append("X");
                fullGuessLine.Append(" ");
            }

            // padd with spaces
            int totalLengthOfLine = numberOfCharsInSecondColumn + numberOfCharsInFirstColumn + k_NumberOfSeparatorsInLine;
            fullGuessLine = new StringBuilder(fullGuessLine.ToString().PadRight(totalLengthOfLine));
            fullGuessLine.Remove(fullGuessLine.Length - 1, 1); // removes last not needed space
            fullGuessLine.Append("|");
            System.Console.WriteLine(fullGuessLine);
            drawLineOfTableContainingCharacter('=');
        }

        private void drawEmptyRowOfTable()
        {
            drawLineOfTableContainingCharacter(' ');
            drawLineOfTableContainingCharacter('=');
        }

        private void drawLineOfTableContainingCharacter(char i_FillingCharacter)
        {
            StringBuilder fullLine = new StringBuilder("|");
            int numberOfCharsInFirstColumn = (Guess.k_GuessLength * 2) + 1;
            int numberOfCharsInSecondColumn = (Guess.k_GuessLength * 2) - 1;

            for (int i = 0; i < numberOfCharsInFirstColumn; ++i)
            {
                fullLine.Append(i_FillingCharacter);
            }

            fullLine.Append("|");
            for (int i = 0; i < numberOfCharsInSecondColumn; ++i)
            {
                fullLine.Append(i_FillingCharacter);
            }

            fullLine.Append("|");
            System.Console.WriteLine(fullLine);
        }

        private void printFinalGameMessage(int i_NumberOfGuesses)
        {
            if (m_GameWon)
            {
                System.Console.WriteLine("You guessed after {0} steps!", i_NumberOfGuesses);
            }
            else
            {
                if (m_QuitGame)
                {
                    System.Console.WriteLine("You chose to quit game. You lost.");
                }
                else
                {
                    System.Console.WriteLine("No more guesses allowed. You lost.");
                }
            }
        }
        
        private bool promptPlayAnotherGame()
        {
            bool playAnotherGame = !k_Play;
            bool isValidInput = !k_Valid;
            
            do
            {
                System.Console.WriteLine("Would you like to start a new game? <Y/N>");
                string answer = System.Console.ReadLine();
                if (answer.Equals("Y") || answer.Equals("y"))
                {
                    playAnotherGame = k_Play;
                    isValidInput = k_Valid;
                }
                else
                {
                    if (answer.Equals("N") || answer.Equals("n"))
                    {
                        isValidInput = k_Valid;
                    }

                }

            } while (!isValidInput);

            return playAnotherGame;
        }        
    }
}